/**
 * @program: quartzLearn
 * @description:
 * @author: zyc
 * @create: 2018-08-07 10:32
 **/
package com.zycuse.nettyboot.NettyServer;

import com.zycuse.nettyboot.Config;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Timer;
import java.util.TimerTask;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Service
public class NettyClientTest {
    public static final String HOST = "localhost";
    public static final int PORT = Config.SERVER_PORT;
    public static final String ENCODE = "utf-8";
    private static Channel connet;
    public static void main(String[] args) {
        NettyClientTest nettyClientTest = new NettyClientTest();
        nettyClientTest.startClient();
    }

    /**
     */

    public void startClient() {
        EventLoopGroup workGroup = new NioEventLoopGroup();
        //核心类
        Bootstrap b = new Bootstrap();
        //工作线程组
        b.group(workGroup);
        // 通道类型
        b.channel(NioSocketChannel.class);
        //操作类型
        b.handler(new InitChannel());
        //发起连接
        b.connect(HOST, PORT);
        log.info("connect to " + HOST + ":" + PORT);
        //测试任务 发送字符串 因为有LineBasedFrameDecoder 所以发送字符串需要换行
    }

    /**
     *  参见服务端 这里主要增加了  TcpClientHandler  NewObjectHandler 两个CHannelHandler类
     *
     *  我在TcpClientHandler的测试方法中使用了ctx.fireChannelRead(no);
     *  将封装好的NewObject对象传给NewObjectHandler
     *  在pipeline中注册的ChannelHandler 以链表的方式逐个执行
     *
     *  具体实现方式：
     *  Handler对象绑定了一个ChannelHandlerContext, 通过context.fireChannelRead
     *  调用下一个context包装的Handler的ChannelRead方法
     *
     *  此过程中 需要判断ChannelHandler是否为Inbound 或者 outBound
     *  出站的数据处理类和入栈的数据处理类都绑定在了一个Pipeline上
     *
     */
    private class InitChannel extends ChannelInitializer<Channel> {

        @Override
        protected void initChannel(Channel ch) throws Exception {
            ChannelPipeline pipeline = ch.pipeline();

//            pipeline.addLast(new LineBasedFrameDecoder(128 * 1024));
            pipeline.addLast("spliter", new DelimiterBasedFrameDecoder(1024*1024, Delimiters.lineDelimiter()));
            pipeline.addLast(new StringDecoder(Charset.forName(ENCODE)));
            pipeline.addLast(new TcpClientHandler());
//            pipeline.addLast(new NewObjectHandler());
            pipeline.addLast(new StringEncoder(Charset.forName(ENCODE)));
        }
    }
    private class NewObject{
        String mag ;
        int code;

        public NewObject(String mag, int code) {
            this.mag = mag;
            this.code = code;
        }

        public String getMag() {
            return mag;
        }

        public void setMag(String mag) {
            this.mag = mag;
        }

        public int getCode() {
            return code;
        }

        public void setCode(int code) {
            this.code = code;
        }
    }


    /**
     * 操作类
     */
    @ChannelHandler.Sharable
    private class TcpClientHandler extends SimpleChannelInboundHandler<String> {
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            connet = ctx.channel();
            super.channelActive(ctx);
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
//            System.out.println(msg + msg.length() + msg.equals("<q_key_http://localhost:4564/index/hello>"));
            log.info("recive message is :" + msg);
            String regexPower = "<([a-z])(_[^_]*)(_[^_]*)?(_[^_]*)?>";
            Pattern regexPattern = Pattern.compile(regexPower);
            Matcher matcher = regexPattern.matcher(msg);
            try {
                if (!matcher.find()) {
                    return;
                }
                int regexLength = matcher.groupCount();
                log.info(regexLength + "  " + matcher.group(3) + " " + msg);
                String regex1 = matcher.group(1);
                String regex2 = matcher.group(2).substring(1);
                String regex3 = matcher.group(3).substring(1);
                switch (regex1) {
                    case "q":{
                        URL url = new URL(regex3);
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

                        conn.connect();
                        InputStream is = conn.getInputStream();
                        Reader reader = new InputStreamReader(is);
                        char[] chars = new char[2024];
                        int length = 0;
                        StringBuilder stringBuilder = new StringBuilder();
                        while ((length = reader.read(chars)) != -1) {
                            stringBuilder.append(new String(chars,0,length));

                        }
                        String response = conn.getResponseMessage();
                        log.info(response + " " + conn.getContent().toString());
                        byte[] enMessage = Base64.encodeBase64(stringBuilder.toString().getBytes(CharsetUtil.UTF_8));
                        String asc = new String(enMessage, CharsetUtil.UTF_8);
                        log.info(asc);
//                        ctx.channel().writeAndFlush("<a_" + regex2 + "\n_4564\r\n456645364>");
                        ctx.channel().writeAndFlush("<q_" + regex2 + "_" + asc + ">\n");
                    }
                    default:
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            NewObject no = new NewObject(msg, 1);
        }

        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            ctx.close();
            startClient();
            super.channelInactive(ctx);
        }
    }

    /**
     *  操作类
     */
    private class NewObjectHandler extends SimpleChannelInboundHandler<NewObject> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, NewObject msg) throws Exception {
            System.out.println(msg.code + "  " + msg.mag);
            ctx.fireChannelRead(msg.mag);
        }
    }

}
